#include "strategy-00.hpp"
#include "channel.h"
#include "mockif.hpp"
#include "money-tree.hpp"
#include "schrodinger.hpp"
#include "strategy.hpp"
#include "tjm_action.hpp"
#include "trigger.hpp"
#include "types.h"

static const price_t MIN_GOOD_SAPCE = 15;
static const price_t MAX_GOOD_SPACE = 50;
#define LARGE_OPPORTUNITY 68

#define D1_DYNAMICAL VO->d_dynamical
#define D2_DYNAMICAL VO->d2_dynamical

namespace Trade {

/****************************************************************************\
 *   Strategy00                                                             *
 * 1. 开仓：
 *    1.1）空仓 高概率方向与趋势方向、力度方向一致，并且有预估的盈利空间
 *    1.2）加仓                                                  (`.-,')
 *      1.2.1）只在小于最大持仓量时加仓                           .-'     ;
 *      1.2.2) 在减仓之后，风险较小的时补进                   _.-'   , `,-
 * 2. 反手：只要趋势+力量概率支持反手                   _ _.-'     .'  /._
 * 3. 风控：使用2手仓位，控制风险                    .' `  _.-.  /  ,'._;)
 *                                             (       .  )-| (
 * a. 开仓价格：满足条件时，以当前价格开仓             )`,_ ,'_,'  \_;)
 * b. 平仓价格：                           ('_  _,'.'  (___,))
 *    1）风险大时减仓价格，为局部最大值l2       `-:;.-'
 *    2) 大回撤时，局部最大值的80%
 *    3）最小盈利
 *    4）最大亏损
 *    5）最大盈利
 * -----------------------------
 * 4. 有风险就平一半，快赔光了就全平，没风险再加进去
 *                                                                          *
 ****************************************************************************/
bool
Strategy00::is_good_enter_spot(const dir_t d) const noexcept
{
  if (DIR_MDA != d)
    return false;

  auto golden_price = BLINES.get_golden_point(-1);
  price_t hp_max, hp_min;
  get_max_min_in_mma(VO_MMA, &hp_max, &hp_min);

  auto is_less_equal = [](auto p1, auto p2) { return p1 <= p2 + 3; };

  auto is_great_equal = [](auto p1, auto p2) { return p1 + 3 >= p2; };

  bool yes = false;
  if (VAL_OPPOR > LARGE_OPPORTUNITY) {
    if ((d < 0 && is_less_equal(golden_price, hp_max)) ||
        (d > 0 && is_great_equal(golden_price, hp_min))) {
      yes = true;
    }
  } else if ((d < 0 && is_less_equal(HPNOW, BLINES.hp_dn_2)) ||
             (d > 0 && is_great_equal(HPNOW, BLINES.hp_up_2))) {
    yes = true;
  }

  return yes;
}

bool
Strategy00::is_good_expected_space(const dir_t d,
                                   const bool good_enter) const noexcept
{
  bool yes = false;
  price_t space = 0;
  if (good_enter) {
    //
    // current price to opposite-line2
    //
    space = (BLINES.get_line2_price(d) - HPNOW) * d;
  } else {
    //
    // line-dn1 up to line-up1
    //
    space = BLINES.main_space();
  }
  return yes = (space > MIN_GOOD_SAPCE && space < MAX_GOOD_SPACE);
}

bool
Strategy00::is_good_timer(const dir_t d, const time_t ts) const noexcept
{
  //
  // TODO make it batter
  //
  return true;
}

bool
Strategy00::risk_is_good_to_me(const price_t risk) const noexcept
{
  //
  // TODO Ok Ok, to be smarter
  //

  //
  // CHOOSE if you can not take the risk, then waiting for a good cost price, or
  //        do thing, just waiting ...
  //

  // No at all, I am RICH!
  return true;
}

price_t
Strategy00::calculate_reduce_price() noexcept
{
  price_t l2 =
    (TOM.amount_rl2) ? TOM.reached_line : BLINES.get_line2_price(TOM_DIR);
  return l2;
}

price_t
Strategy00::calculate_close_price() noexcept
{
  price_t hpx = 0;
  hpx = HPNOW;

  return hpx;
}

const price_t CLOSE_PRICE_DELTA = 5;

price_t
Strategy00::calculate_min_win_price() noexcept
{
  const price_t rvn_min_win =
    8 + HOP_FOR_COMMISION; // close all  , 5 for commision
  int vol;
  price_t avg_hpx;
  if (MOCKIF.mock_query_position(TOM_DIR, &vol, &avg_hpx)) {
    price_t hp_min_win = avg_hpx + rvn_min_win * TOM_DIR;
    price_t old_min_win = TRIGGERS.get_close_price_min_win();
    if (old_min_win > 0) {
      if (std::abs(old_min_win - hp_min_win) < CLOSE_PRICE_DELTA)
        hp_min_win = -1;
    }
    return hp_min_win;
  }
  return -1;
}

price_t
Strategy00::calculate_stop_win_price() noexcept
{
  const price_t rvn_stop_win = 150;
  price_t hpx = TOM.my_hp + rvn_stop_win * TOM_DIR;
  const auto old = TRIGGERS.get_close_price_stop_win();
  if (old > 0) {
    if (std::abs(old - hpx) < CLOSE_PRICE_DELTA)
      hpx = -1;
  }
  return hpx;
}

price_t
Strategy00::calculate_stop_loss_price() noexcept
{
  const price_t rvn_stop_loss = -150;
  price_t hpx = TOM.my_hp + rvn_stop_loss * TOM_DIR;
  const auto old = TRIGGERS.get_close_price_stop_loss();
  if (old > 0) {
    if (std::abs(old - hpx) < CLOSE_PRICE_DELTA)
      hpx = -1;
  }
  return hpx;
}
int
Strategy00::should_add_position() noexcept
{
  return (0);
}

int
Strategy00::should_deduce_position() noexcept
{
  return (0);
}

bool
Strategy00::should_close_all_position() noexcept
{
  return (0);
}

bool
Strategy00::can_i_safe_myself() noexcept
{
  return (0);
}

int
Strategy00::safe_me_please() noexcept
{
  return (0);
}

int
Strategy00::tom_run(const dir_t d, const price_t enter) noexcept
{
  jerry_stop();
  mickey_stop();
  int ret = tjm->action_tom_run(d, enter, BLINES, 0);
  if (ret) {
    send_action(IDX, TMNOW, 1, 3, d, HPNOW, 0);
    // clm.init_position(d);
    TRIGGERS.set_open_price(d, enter, VO, mt);
  }
  return ret;
}

int
Strategy00::tom_stop() noexcept
{
  const dir_t d = TOM_DIR;
  jerry_stop();
  TRIGGERS.unset_all();
  tjm->action_tom_stop(HPNOW, 0);
  send_action(IDX, TMNOW, 1, 4, d, HPNOW, 0);
  return mickey_run(DIR_OPPOR, HPNOW);
}

int
Strategy00::jerry_run(const dir_t d, const price_t enter) noexcept
{
  int ret = tjm->action_jerry_run(d, BLINES, 0);
  send_action(IDX, TMNOW, 2, 3, d, HPNOW, 0);
  deduce_position_();
  return ret;
}

int
Strategy00::deduce_position_() noexcept
{
  int vol = MOCKIF.mock_query_position_volume(TOM_DIR);
  if (vol) {
    if (vol / 2 > min_vol_in_hand) {
      int reduce_vol = vol / 2;
      if (reduce_vol && !reduce_time_) {
        reduce_time_++;
        reduce_price_ = calculate_reduce_price();
        set_close_(reduce_price_);
        JERRY.status = TOM.status = PET_STATUS::REDUCE_POSITION;
      }
    }
  }
  return vol;
}

int
Strategy00::jerry_stop() noexcept
{
  send_action(IDX, TMNOW, 2, 4, JERRY_DIR, HPNOW, 0);
  tjm->action_jerry_stop(0);
  return (0);
}

int
Strategy00::mickey_run(const dir_t d, const price_t enter) noexcept
{
  if (!d)
    return (0);
  int ret = tjm->action_mickey_run(d, enter, BLINES, 0);
  // send_action(IDX, TMNOW, 3, 3, d, HPNOW, 0);
  return ret;
}

int
Strategy00::mickey_stop() noexcept
{
  // send_action(IDX, TMNOW, 3, 4, MICKEY_DIR, HPNOW, 0);
  int ret = tjm->action_mickey_stop(0);
  return ret;
}

int
Strategy00::get_open_volume() noexcept
{
  int v = 4;
  return v * vol_per_unit;
}

int
Strategy00::get_close_volume() noexcept
{
  return (0);
}

event_t
Strategy00::on_order_open(const OrderPtr& o, const DealPtr& deal) noexcept
{
  TOM.status = PET_STATUS::OPENED;
  send_action(IDX, TMNOW, 0, 1, o->d, o->hpx, o->vol);
  return (0);
}

event_t
Strategy00::on_order_close(const OrderPtr& o, const DealPtr& deal) noexcept
{
  if (TOM.status == PET_STATUS::REDUCE_POSITION) {
    //
    // Check All volume are filled (全部成交), but in mock-if, we assume YES
    //
    TOM.status = PET_STATUS::NORMAL;
  } else if (TOM.status == PET_STATUS::CLOSE_MIN_WIN) {
    tom_stop();
    reduce_time_ = 0;
    reduce_price_ = 0;
  }
  send_action(IDX, TMNOW, 0, 0, o->d, o->hpx, o->vol);
  return (0);
}

int
Strategy00::jerry_in_dangerous() noexcept
{
  if (TOM.status == PET_STATUS::REDUCE_POSITION) {
    //
    // close 1/2， the close order has pleased already.
    //
  } else if (TOM.status == PET_STATUS::NORMAL) {
    //
    // close 1/2
    //
    set_close_min_win_();
  }

  return (0);
}

int
Strategy00::earned_good_enough_space(const dir_t d) const noexcept
{
  int ret = 0;
  return ret;
}

int
Strategy00::pet_time_outed(const dir_t d) const noexcept
{
  int ret = 0;
  return ret;
}

int
Strategy00::on_open(const Trigger* trigger) noexcept
{
  if (VO->consistent_visual_dir(TOM_DIR)) {
    MOCKIF.mock_recall_all_order(IDX);
    int vol = get_open_volume();
    MOCKIF.mock_place_open_order(TOM_DIR, trigger->hpx, HPNOW, vol, TMNOW, IDX);
  } else {
    tom_stop();
  }

  send_action(IDX, HPNOW, 4, 1, -trigger->dir, trigger->reached_hpx, 0);
  return (0);
}

int
Strategy00::on_close(const Trigger* trigger) noexcept
{
  auto is_reduce_position = [&]() {
    return (trigger->etype == TRIGGER_EVENT_TYPE::CLOSE) &&
           (TOM.status == PET_STATUS::REDUCE_POSITION);
  };

  if (is_reduce_position()) {
    int vol = MOCKIF.mock_query_position_volume(TOM_DIR) / 2;
    if (vol) {
      MOCKIF.mock_place_close_order(
        TOM_DIR, trigger->hpx, HPNOW, vol, TMNOW, IDX);
    }
  }

  return (0);
}

int
Strategy00::on_close_stop_win(const Trigger* trigger) noexcept
{
  return (0);
}

int
Strategy00::on_close_stop_loss(const Trigger* trigger) noexcept
{
  return (0);
}

int
Strategy00::on_close_min_win(const Trigger* trigger) noexcept
{
  auto is_min_win = [&]() {
    return (trigger->etype == TRIGGER_EVENT_TYPE::CLOSE_MIN_WIN);
  };

  if (is_min_win()) {
    int vol = MOCKIF.mock_query_position_volume(TOM_DIR);
    if (vol) {
      MOCKIF.mock_recall_all_order(IDX);

      MOCKIF.mock_place_close_order(
        TOM_DIR, trigger->hpx, HPNOW, vol, TMNOW, IDX);
      TOM.status = PET_STATUS::CLOSE_MIN_WIN;
    }
  }

  return (0);
}

int
Strategy00::on_mark_price_up(const Trigger* trigger) noexcept
{
  return (0);
}

int
Strategy00::on_mark_price_dn(const Trigger* trigger) noexcept
{
  send_action(IDX, HPNOW, 4, 2, trigger->dir, trigger->reached_hpx, 0);
  return (0);
}

int
Strategy00::on_mark_timer_out(const Trigger* trigger) noexcept
{
  send_action(IDX, HPNOW, 4, 2, 3, trigger->reached_hpx, 0);
  return (0);
}

int
Strategy00::on_mark_price_(const Trigger* trigger) noexcept
{
  return (0);
}

int
Strategy00::close_half_or_all_(const bool dangerous,
                               const bool reached_min_win,
                               const bool keep_tom,
                               const dir_t d,
                               const price_t hpx) noexcept
{
  int vol = MOCKIF.mock_query_position_volume(d);
  int ret = 1;
  if ((!dangerous && !reached_min_win) || keep_tom) {
    vol = vol / 2;
    ret = 2;
  } // if vol == 1, then keep it
  if (vol) {
    MOCKIF.mock_place_close_order(d, hpx, HPNOW, vol, TMNOW, IDX);
  } else {
    ret = 0;
  }
  return ret;
}

int
Strategy00::set_close_min_win_() noexcept
{
  auto min_win = calculate_min_win_price();
  if (min_win > 0) {
    TRIGGERS.set_close_min_win(TOM_DIR, min_win);
    //
    // TODO unset or reset other close-price ?
    //

    send_action(IDX, HPNOW, 4, 0, TOM_DIR, min_win, 0);
  }
  return (0);
}

int
Strategy00::set_close_stop_loss_() noexcept
{
  auto stop_loss = calculate_stop_loss_price();
  TRIGGERS.set_close_stop_loss(TOM_DIR, stop_loss);
  //
  // TODO unset or reset other close-price ?
  //
  send_action(IDX, HPNOW, 4, 0, TOM_DIR, stop_loss, 0);
  return (0);
}

int
Strategy00::set_close_stop_win_() noexcept
{
  auto stop_win = calculate_stop_win_price();
  TRIGGERS.set_close_stop_win(TOM_DIR, stop_win);
  //
  // TODO unset or reset other close-price ?
  //
  send_action(IDX, HPNOW, 4, 0, TOM_DIR, stop_win, 0);
  return (0);
}

int
Strategy00::set_close_(const price_t hpx) noexcept
{
  TRIGGERS.unset_open_price();

  bool set = true;
  price_t hp_marked =
    TRIGGERS.get_trigger(TRIGGER_EVENT_TYPE::CLOSE, TRIGGER_TYPE::SPACE)->hpx;
  if (hp_marked > 0 && (hpx - hp_marked) * TOM_DIR < 5) {
    // ignore
    set = false;
  } else {
    // we can win
    if (MOCKIF.is_good_close_price(TOM_DIR, hpx)) {
      set = true;
    } else {
      //
      // we are losing
      //
      //
      // I HAVE A close lost
      // can i waiting for a little while?
      // or
      // i will take this lost
      //
    }
  }

  if (set) {
    TRIGGERS.set_close_price(TOM_DIR, hpx);
    MOCKIF.mock_recall_all_open_order(TOM_DIR, IDX);
    send_action(IDX, HPNOW, 4, 0, TOM_DIR, hpx, 0);
  }

  return set;
}

bool
Strategy00::reached_target_space(const dir_t d) const noexcept
{
  bool yes = false;
  if (TOM.reached_line2()) {
    price_t hp_min, hp_max;
    get_max_min_in_mma(VO_MMA, &hp_max, &hp_min);
    yes = (TOM_DIR) ? hp_min < TOM.target.lines.hp_dn_2
                    : hp_max > TOM.target.lines.hp_up_2;
  } else if (TOM.amount_rl2 > 0) {
    // const auto p1 = BLINES.hp_dn_2;
    // const auto p2 = HPNOW;
    price_t hp_min, hp_max;
    get_max_min_in_mma(VO_MMA, &hp_max, &hp_min);
    yes = (TOM_DIR < 0) ? hp_min < TOM.reached_line : hp_max > TOM.reached_line;
  }
  return yes;
}

bool
Strategy00::is_strong_opportunity(const dir_t d) const noexcept
{
  return (DIR_OPPOR == d && VAL_OPPOR > 68);
}

bool
Strategy00::visual_dir_against_dir(const dir_t d) const noexcept
{
  bool against = false;
  // dir_t d_stable = VO->oppor_trend.get_stable_dir();
  // // percent_t pct = VO->oppor_trend.get_stable_percent();
  // if (d_stable != d) {
  //   if (DIR_MDA == -d) {
  //     against = true;
  //   }
  // }
  if (DIR_MDA == -d) {
    against = true;
  }
  return against;
}

inline int
Strategy00::try_jerry_() noexcept
{
  return (JERRY.is_running()) ? jerry_in_dangerous()
                              : jerry_run(TOM_DIR, HPNOW);
}

int
Strategy00::money_tree() noexcept
{
  int ret = 0;
  dir_t d_guess = mt->d_guess;

  if (mt->mt_event & EVENT_FILTER_EVENT) {
    debug_history();

    if (TOM.is_running()) {
      if (mt->d_guess != d_guess) {
        if (reached_target_space(d_guess)) {
          if (visual_dir_against_dir(d_guess)) {
            //
            // reverse the direction
            // printf("REverse\n");
            //
            // stop_tom
            // find a enter to reverse
            //
            if (is_good_enter_spot(mt->d_guess)) {
              do_something();
            } else {
              ret = try_jerry_();
            }
          } else {
            ret = try_jerry_();
          }
        }
      }
      // else {
      //   ignore
      // }
    } else {
      if (is_good_enter_spot(mt->d_guess)) {
        if (is_good_expected_space(mt->d_guess, true)) {
          //
          // For Now, We did not waiting a better price
          // auto enter_price = BLINES.get_golden_point(-1);
          //
          tom_run(mt->d_guess, HPNOW);
        }
      } else {
        auto get_good_price = [&](const dir_t d) {
          if (VAL_OPPOR >= LARGE_OPPORTUNITY) {
            return BLINES.get_golden_point(-1);
          } else {
            return BLINES.get_line2_price(-d);
          }
        };

        auto good_price = get_good_price(mt->d_guess);
        tom_run(mt->d_guess, good_price);
      }
    }
  }

  return ret;
}

int
Strategy00::set_mark_price_(const dir_t d, const price_t hpx) noexcept
{
  if (d > 0) {
    TRIGGERS.set_mark_price_up(hpx, VO, mt, nullptr);
  } else {
    TRIGGERS.set_mark_price_dn(hpx, VO, mt, nullptr);
  }
  return (0);
}

void
Strategy00::debug_history() noexcept
{
  printf("OS2:\n");
  const auto& os2 = VO->os2;
  for (usize_t i = 0; i < os2.size; ++i) {
    const auto& o = os2.data[i];
    o.print(HPNOW, TMNOW);
  }

  printf("BIGO:\n");
  const auto& b = VO->bor;
  for (usize_t i = 0; i < b.size; ++i) {
    const auto& bo = b.data[i];
    bo.print(HPNOW, TMNOW);
  }

  printf("Force Near:\n");
  const auto& fn = VO->fs_near;
  for (usize_t i = 0; i < fn.size; ++i) {
    const auto& f = fn.data[i];
    f.print(HPNOW, TMNOW);
  }

  printf("Force Far:\n");
  const auto& ff = VO->fs_far;
  for (usize_t i = 0; i < ff.size; ++i) {
    const auto& f = ff.data[i];
    f.print(HPNOW, TMNOW);
  }
}
} // namespace TradeE